#include "xMappingNode.h"
#include "xNodeType_Math.h"
#include "xParameterPack.h"
#include "all_entries.h"
#include "ArithFactory.h"
#include "MyScanner.h"
#include "MyParser.h"
#include "xNodeType_WOP.h"
#include "xInMappingPort.h"
#include "xOutMappingPort.h"

#include <sstream>

xNodeType_Math::xNodeType_Math(){

	this->_wop=NULL ;
	this->_haveFormula=false;
	this->_name = "math" ;
	this->_root = NULL;
}

xNodeType_Math::~xNodeType_Math(){
	
	clearTree(this->_root);
}

void xNodeType_Math::setupPorts() {

	assert(_parent != NULL);

	_parent->_inports.push_back(new xInMappingPort(_parent,"wop","wop",CLASS_REQUIRED,"NULL",CATEGORY_OTHER));
	_parent->_outports.push_back(new xOutMappingPort(_parent,"wop","wop",CLASS_REQUIRED,"NULL",CATEGORY_OTHER ));
}


void xNodeType_Math::createMappedObject() {

	xMappingNode *node = this->getParent();

	// Many objects may connect to this mapping node. 
	// Now, we collect all the objects and pack it into a xParameterPack object.
	_xpp.no = 0 ;
	for (int i = 0 ; i < node->getInportSize(); i++) {
		// get the nodetype pointer that connects to this inport (unique)
		xInMappingPort *inp = node->getInPort(i);
		xParameter xp = inp->toXParameter();
		_xpp.no ++;
		_xpp.paras.push_back(xp);

		if (inp->getLink() != NULL) _xpp.para_onoff.push_back(true);
		else _xpp.para_onoff.push_back(false);

	}
	_wop = this->eval();

}

xParameter xNodeType_Math::toXParameter(string type, xOutMappingPort* linked_op) {
	// OK, a Arithmetic node is essentially similar to a wop-type xmapping ndoe
	// when this node is connect to a parent P
	// P can ask for its wop entry, integer pointer, integer value, etc.
	// we should return the correct content according to toType 
	xParameter xp ;
	if (type == "wop") {
		//assert(false);
		xp.type = T_WOP ;
		xp.wop_nodetype = this ;
		xp.val_wop = _wop;
	} else if (type == "varname") {
		xp.type = T_STRING ;
		xp.val_string = _wop->getName();
	} else if (type == "int*") {
		xp.type = TP_INT ;
		if( WOP_Int_Proxy * iwop = dynamic_cast< WOP_Int_Proxy * >( this->_wop ) ) {
			xp.val_int_ptr = iwop->getIntValAddr();
		} else if( WOP_Double_Proxy * dwop = dynamic_cast< WOP_Double_Proxy * >( this->_wop ) ) {
			xp.val_int_ptr = dwop->getIntValAddr();
		} else if( WOP_Float_Proxy * fwop = dynamic_cast< WOP_Float_Proxy * >( this->_wop ) ) {
			xp.val_int_ptr = fwop->getIntValAddr();
		} else {
			assert( !"can not convert to int*" );
		}
		//xp.original = _wop->getVal();
	} else if (type == "int") {
		xp.type = T_INT ;
		WOP_Int_Proxy * iwop = dynamic_cast<WOP_Int_Proxy*>(_wop);
		//if dynamic cast fail, try cast to double or float
		if (iwop == NULL){
			WOP_Double_Proxy * dwop = dynamic_cast<WOP_Double_Proxy*>(_wop);
			xp.val_int_ptr = dwop->getIntValAddr();
			if ( dwop == NULL ){
				WOP_Float_Proxy * fwop = dynamic_cast<WOP_Float_Proxy*>(_wop);
				xp.val_int_ptr = fwop->getIntValAddr();
				assert(fwop != NULL) ;
			}
		}else {
			int *temp = iwop->getIntValAddr();
			xp.val_int = (*temp);
		}
	} else if (type == "bool*") {
		xp.type = TP_BOOL ;
		WOP_Bool_Proxy * bwop = dynamic_cast<WOP_Bool_Proxy*>(_wop);
		assert(bwop != NULL) ;
		xp.val_bool_ptr = bwop->getBoolValAddr();
		//xp.original = bwop->getVal();	
	} else if (type == "float*") {
		xp.type = TP_FLOAT ;
		WOP_Float_Proxy * fwop = dynamic_cast<WOP_Float_Proxy*>(_wop);
		//if dynamic cast fail, try cast to double or int
		if (fwop == NULL){
			WOP_Double_Proxy * dwop = dynamic_cast<WOP_Double_Proxy*>(_wop);
			xp.val_float_ptr = dwop->getFloatValAddr();
			if ( dwop == NULL ){
				WOP_Int_Proxy * iwop = dynamic_cast<WOP_Int_Proxy*>(_wop);
				xp.val_float_ptr = iwop->getFloatValAddr();
				assert(iwop != NULL) ;
			}
		}else {
			xp.val_float_ptr = fwop->getFloatValAddr();
		}
	} else if (type == "double*") {
		xp.type = TP_DOUBLE ;
		WOP_Double_Proxy * dwop = dynamic_cast<WOP_Double_Proxy*>(_wop);
		//if dynamic cast fail, try cast to int or float
		if (dwop == NULL){
			WOP_Float_Proxy * fwop = dynamic_cast<WOP_Float_Proxy*>(_wop);
			xp.val_double_ptr = fwop->getDoubleValAddr();
			if ( fwop == NULL ){
				WOP_Int_Proxy * iwop = dynamic_cast<WOP_Int_Proxy*>(_wop);
				xp.val_double_ptr = iwop->getDoubleValAddr();
				assert(iwop != NULL) ;
			}
		}else{
			xp.val_double_ptr = dwop->getDoubleValAddr();
		}
	} else if (type == "char*") {
		xp.type = TP_CHAR ;
		WOP_Char_Proxy * bwop = dynamic_cast<WOP_Char_Proxy*>(_wop);
		assert(bwop != NULL) ;
		xp.val_char_ptr = bwop->getCharValAddr();
	} else if (type == "ints") { 
		// this is the case where a composite VM asking for dimension
		assert(dynamic_cast<WOP_Array_Entry*>(_wop) != NULL) ;
		WOP_Array_Entry * ary = (WOP_Array_Entry*) _wop ;
		xp.type = T_INTS ;
		xp.val_ints = ary->getDimensionRange();
	} else if (type == "string") {
		xp.type = T_STRING ;
		xp.val_string = _wop->getVal();
	} else if (type == "string*") {
		xp.type = TP_STRING ;
		xp.val_string_ptr = _wop->getValAddr();
	} else if (type == "tostring*") {
		xp.type = TP_STRING ;
		xp.val_string_ptr = _wop->getValAddr();
	} else 
		assert(false);
	// This assertion false occurs due to
	// 
	return xp ;
}


bool xNodeType_Math::update(){ 

	return this->updateEntry(); 
}

string xNodeType_Math::isFormulaValidate(string formula,bool flag){
	
	int size;
	if(flag==true)
		size=_parent->_inports.size()-1;
	else
		size=-1;
	//是否符合scanner的規則
	string result=MyScanner::getInstance()->isLexical(formula,size);
	if(result!="TRUE")
		return result;

	//取得子字串
	queue<MyWord*>* tokens=MyScanner::getInstance()->getTokens();

	//是否符合parser的規則
	result=MyParser::getInstance()->isSyntax(tokens);

	if(result!="TRUE")
		return result;

	//取得數學方程式的tree root
	this->_root=MyParser::getInstance()->getRoot();
	this->_haveFormula=true;
	this->_formula=formula;

	return "TRUE";
}

// string xNodeType_Math::isFormulaValidate( string formula ){
// 	
// 	return this->isFormulaValidate(formula,-1);
// }

WOP_Entry* xNodeType_Math::eval() {

	assert(this->_root);

	WOP_Entry* entry ;
	int last = _xpp.no;
	string name = "Math(" + _xpp.paras[0].type + "~" + _xpp.paras[last-1].type + ")";
	ostringstream oss;
	MyWord* result=this->computeValue(this->_root);

	//型態的判別
	if(result->_type=="int"){
		entry=new WOP_Int_Proxy(new WOP_Int_Entry(name,NULL));
		oss<<result->_value_int;
	}
	else if(result->_type=="double"){
		entry=new WOP_Double_Proxy(new WOP_Double_Entry(name,NULL));
		oss<<result->_value_double;
	}
	else if(result->_type=="float"){
		entry=new WOP_Float_Proxy(new WOP_Float_Entry(name,NULL));
		oss<<result->_value_float;
	}
	else{
		assert(false);
	}

	entry->updateRealValue(oss.str());
	return entry;
}

bool xNodeType_Math::updateEntry() {

	MyWord* result=this->computeValue(this->_root);
	ostringstream oss;

	//型態的判別
	if(result->_type=="int"){
		oss<<result->_value_int;
	}
	else if(result->_type=="double"){
		oss<<result->_value_double;
	}
	else if(result->_type=="float"){
		oss<<result->_value_float;
	}
	else{
		assert(false);
	}
	
	//如果跟舊的數值不一樣則update
	if(oss.str()!=_wop->getVal()){
		_wop->updateRealValue(oss.str());
		return true;
	}
	return false;

}

MyWord* xNodeType_Math::computeValue(MyWord* current){
	
	if(current->_childs!=NULL){

		vector<MyWord*>::iterator itr=current->_childs->begin();
		vector<MyWord*>* childs=new vector<MyWord*>();

		for(;itr!=current->_childs->end();itr++){
			childs->push_back(computeValue(*itr));
		}
		assert(current->_type=="arith");
		return ArithFactory::getInstance()->computeArith(current->_value_string,childs); 
		//return af.computeArith(current->_value_string,childs); 

	}

	//這邊必須要處理當是id也就是連進來的變數的狀況，不直接在MyWord作一個資料型態是xp的原因是
	//當是wop的狀況時，內部存的值都是string，必須要判別型態後作轉型的處理
	//除此之外鬆散耦合是最大的原因
	if(current->_type=="id"){
		int number;
		//取得是第幾個變數
		istringstream(current->_value_string.substr(1))>>number;
		xParameter xp=_xpp.paras[number];
		if(xp.type=="int*"){
			ostringstream os;	
			os<<*xp.val_int_ptr;
			return new MyWord("int",os.str());
		}
		else if(xp.type=="float*"){
			ostringstream os;
			os<<*xp.val_float_ptr;
			return new MyWord("float",os.str());
		}
		else if(xp.type=="double*"){
			ostringstream os;
			os<<*xp.val_double_ptr;
			return new MyWord("double",os.str());
		}
		else if(xp.type=="wop"){
			string type=xp.val_wop->getTypeStr();
			if (type == "bool") { // the $x is connected to a boolean variable
				                  // we translate it to 0 (for false) and 1 (for true)
				if (xp.val_wop->getVal() == "true") return new MyWord("int","1");
				else if (xp.val_wop->getVal() == "false") return new MyWord("int","0");
				assert(false); // should not be here
			}
			assert(type=="int"||type=="double"||type=="float");
			return new MyWord(type,xp.val_wop->getVal());
		}
		else
			assert(false);
	}
	return new MyWord(*current);
}

vector<string> xNodeType_Math::stringSplit(string str, string delim){

	int cutAt;

	vector<string> results;

	while( (cutAt = str.find_first_of(delim)) != str.npos ){
		if(cutAt > 0){
			results.push_back(str.substr(0,cutAt));
		}
		str = str.substr(cutAt+1);
	}
	if(str.length() > 0){
		results.push_back(str);
	}
	return results;

}

bool xNodeType_Math::haveFormula(){
	
	return this->_haveFormula;
}

void xNodeType_Math::clearTree( MyWord* current ){

	if(current != NULL){
		if(current->_childs != NULL){
			vector<MyWord*>::iterator itr=current->_childs->begin();
			for(;itr!=current->_childs->end();itr++){
				clearTree(*itr);
			}
		}
		delete current;
	}	
}

void xNodeType_Math::removeFormula(){

	if(this->_root!=NULL){
		clearTree(this->_root);
		this->_root=NULL;
	}
	this->_haveFormula=false;
}
string xNodeType_Math::valid_rhs_pair[][2] = { // the left is the woptype of this node, right is the link that connects to
"int", "int*", 
"int", "float*", // the integer value is casted to a float 
"int", "double*",
"float","int*",   // the float value is truncated to integer, information could be lost
"float","float*",
"float","double*",
"double","int*",
"double","float*",
"double","double*",
"wop","wop",
"dontcare","tostring*",
"dontcare","wop"
};

bool xNodeType_Math::verify_rhs_link(xOutMappingPort *left, xInMappingPort *right, xNodeType* rightnode) {

	if (_wop == NULL) { // in many cases, the mapping node may not have a _wop object yet
						// before the link is checked in this case, we check
		if (right->getType() == "int*" || 
			right->getType() == "float*" ||
			right->getType() == "double*" ||
			right->getType() == "tostring*" ||
			right->getType() == "wop")
			return true ;
	}
	string wop_type = _wop->getTypeStr() ;
	return check_valid_pair(valid_rhs_pair, sizeof(valid_rhs_pair)/(sizeof(string)*2),
							wop_type, right->getType());

}
string xNodeType_Math::gate_pair[][2] = {
"dontcare","wop",
"dontcare","int*",
"dontcare","double*",
"dontcare","float*",
"dontcare","bool*",
"dontcare","char*"
};
string xNodeType_Math::wop_pair[][2] = { // the left is the woptype of this node, right is the link that connects to
"dontcare","int",
"dontcare","double",
"dontcare","float",
"dontcare","bool",
"dontcare","char"
};
string xNodeType_Math::other_pair[][2] = {
"dontcare","int*",
"dontcare","double*",
"dontcare","float*",
"dontcare","int",
"dontcare","double",
"dontcare","float",
"dontcare","wop"
};

bool xNodeType_Math::verify_lhs_link(xOutMappingPort *left, xNodeType* leftnode_nt, xInMappingPort *right) {
	if (leftnode_nt->getType() == "gate") {
		// if the lhs is a gate port, 
		return check_valid_pair(gate_pair,sizeof(gate_pair)/(sizeof(string)*2),
								"dontcare",left->getType());
	}
	if (leftnode_nt->getType() == "wop") { // if the leftnode is a wop, we want to check if
		                              //   its type is int,
		xNodeType_WOP *wopnt = (xNodeType_WOP*)(leftnode_nt);
		WOP_Entry *wop = wopnt->getWOP();
		return check_valid_pair(wop_pair,sizeof(wop_pair)/(sizeof(string)*2),
								"dontcare",wop->getTypeStr());
	}
	// to here left is often the outport of other mapping node
	// we limit those ports to be of type int*, double*,
	return check_valid_pair(other_pair,sizeof(other_pair)/(sizeof(string)*2),
							"dontcare",left->getType());
}
string xNodeType_Math::toMDString(MDSType type) {

	ostringstream MDSstr ;
	if ( type == xNodeType::NormalMDSFormat ){
		MDSstr << "node "<<  _parent->getSerialNo() << " math "<< "@ " << _formula << " @" << endl ; 
	} else if ( type == xNodeType::ParameterizedMDSFormat){
		MDSstr << "ADD_NODE " <<"node "<< "SN" <<_parent->getSerialNo() << " math "<< "@ " << _formula << " @" << endl ; 
	} else {
		assert(false);
	}
	
	return MDSstr.str();
}
int xNodeType_Math::parseMDString(vector<string>& tokens, int start_index) {
	this->setInportFixed(false);
	this->setOutportFixed(true);
	
	int i = start_index ;
	assert(tokens[i] == "node");
	string serialno = tokens[i+1] ;
	assert(tokens[i+2] == "math");
	assert(tokens[i+3] == "@");
	int k = 0 ;
	string formula = "" ;
	do { // a formula may have spaces  
		formula = formula + tokens[i+4+k];
		k++ ;
	} while (tokens[i+4+k] != "@");
	
	this->isFormulaValidate(formula,false);
	this->getParent()->deletePort(0);
	return 5+k ; // five tokens are 
}

string xNodeType_Math::getFormula()
{
	return _formula;
}
